package org.sgx.jsutil.client;

import java.util.Collection;
import java.util.Iterator;

import com.google.gwt.core.client.JavaScriptObject;

/**
 * <p>
 * a JsArray Java overlay type different from provided by GWT's : JsArray, JsArrayMixed, JsArrayString, JsArrayNumber.
 * </p>
 * 
 * <p>
 * With this JsArray class one can reference an array with items of any type,. It comes with generics for working with single-type arrays, but also support mixed types arrays
 * (JsArray&lt;Object>)
 * </p>
 * 
 * @param T
 *            - for single type arrays T is the item's type. Use T == Object.class for mixed type arrays.
 * 
 * @author sg
 * 
 */
public class JsArray extends JavaScriptObject {
	
	protected JsArray() {
	}
	
	public static final native JsArray create(JavaScriptObject...t)/*-{
		return @org.sgx.jsutil.client.JsUtil::toJsArray([Lcom/google/gwt/core/client/JavaScriptObject;)(t); 
	}-*/;
	
//	public static final native JsArray create(int...t)/*-{		
//		return @org.sgx.jsutil.client.JsUtil::toJsArrayInt([I)(t); 
//	}-*/;
	
	public static final native JsArray create(double...t)/*-{		
		return @org.sgx.jsutil.client.JsUtil::toJsArrayDouble([D)(t); 
	}-*/;
	
	
//	public static final native JsArray<Integer> create(int[]t)/*-{
//		$wnd.alert("hi: "+t[0]); 
//		if(t==null||t.length==0)
//			return [];
//		else 
//			return @org.sgx.jsutil.client.JsUtil::toJsArrayInt([I)(t);  
//	}-*/;
	
	public static final native JsArray create(String...t)/*-{	
		return @org.sgx.jsutil.client.JsUtil::toJsArrayString([Ljava/lang/String;)(t); 
			 
	}-*/;
	
	public static final native JsArray create(boolean...t)/*-{
		return @org.sgx.jsutil.client.JsUtil::toJsArrayBoolean([Z)(t); 
	}-*/;
	
	
	/**
	 * Convert each element of the array to a String and join them with a comma separator. The value returned from this method may vary between browsers based on how JavaScript
	 * values are converted into strings.
	 */
	public final String join() {
		// As per JS spec
		return join(",");
	}

	/**
	 * Convert each element of the array to a String and join them with a comma separator. The value returned from this method may vary between browsers based on how JavaScript
	 * values are converted into strings.
	 */
	public final native String join(String separator) /*-{
		return this.join(separator);
	}-*/;

	/**
	 * Gets the length of the array.
	 * 
	 * @return the array length
	 */
	public final native int length() /*-{
		return this.length;
	}-*/;

	/**
	 * Pushes the given value onto the end of the array.
	 */
	public final native void push(JavaScriptObject value) /*-{
		this.push(value); 
	}-*/;
	/**
	 * Pushes the given value onto the end of the array.
	 */
	public final native void push(int value) /*-{
		this.push(value); 
	}-*/;
	/**
	 * Pushes the given value onto the end of the array.
	 */
	public final native void push(String value) /*-{
		this.push(value); 
	}-*/;
	/**
	 * Pushes the given value onto the end of the array.
	 */
	public final native void push(boolean value) /*-{
		this.push(value); 
	}-*/;	
	/**
	 * Pushes the given value onto the end of the array.
	 */
	public final native void push(double value) /*-{
		this.push(value); 
	}-*/;
	
//	/**
//	 * get the i-th item of this array
//	 */
//	public final native T get(int i) /*-{
//		return @org.sgx.jsutil.client.JsUtil::castToObject(Ljava/lang/Object;)(this[i]);  
//	}-*/;
	
	/**
	 * get the i-th item of this array
	 */
	public final native String getString(int i) /*-{
		return this[i]; 
	}-*/;
	/**
	 * get the i-th item of this array
	 */
	public final native int getInt(int i) /*-{
		return this[i]; 
	}-*/;
	/**
	 * get the i-th item of this array
	 */
	public final native <T extends JavaScriptObject> T get(int i) /*-{
		return this[i]; 
	}-*/;
	/**
	 * get the i-th item of this array
	 */
	public final native double getDouble(int i) /*-{
		return this[i]; 
	}-*/;
	/**
	 * get the i-th item of this array
	 */
	public final native boolean getBoolean(int i) /*-{
		return this[i]; 
	}-*/;	
//	/**
//	 * get the i-th item of this array
//	 */
//	public final native <B> B getAny(int i) /*-{
//		return this[i]; 
//	}-*/;
	/**
	 * get the i-th item of this array
	 */
	public final native JavaScriptObject getObject(int i) /*-{
		return this[i]; 
	}-*/;
	
	/**
	 * Sets the object value at a given index.
	 * 
	 * If the index is out of bounds, the value will still be set. The array's length will be updated to encompass the bounds implied by the added object.
	 * 
	 * @param index
	 *            the index to be set
	 * @param value
	 *            the object to be stored
	 */
	public final native void set(int index, int value) /*-{
		this[index] = value;
	}-*/;
	/**
	 * Sets the object value at a given index.
	 * 
	 * If the index is out of bounds, the value will still be set. The array's length will be updated to encompass the bounds implied by the added object.
	 * 
	 * @param index
	 *            the index to be set
	 * @param value
	 *            the object to be stored
	 */
	public final native void set(int index, String value) /*-{
		this[index] = value;
	}-*/;
	/**
	 * Sets the object value at a given index.
	 * 
	 * If the index is out of bounds, the value will still be set. The array's length will be updated to encompass the bounds implied by the added object.
	 * 
	 * @param index
	 *            the index to be set
	 * @param value
	 *            the object to be stored
	 */
	public final native void set(int index, boolean value) /*-{
		this[index] = value;
	}-*/;
	/**
	 * Sets the object value at a given index.
	 * 
	 * If the index is out of bounds, the value will still be set. The array's length will be updated to encompass the bounds implied by the added object.
	 * 
	 * @param index
	 *            the index to be set
	 * @param value
	 *            the object to be stored
	 */
	public final native void set(int index, JavaScriptObject value) /*-{
		this[index] = value;
	}-*/;
	/**
	 * Sets the object value at a given index.
	 * 
	 * If the index is out of bounds, the value will still be set. The array's length will be updated to encompass the bounds implied by the added object.
	 * 
	 * @param index
	 *            the index to be set
	 * @param value
	 *            the object to be stored
	 */
	public final native void set(int index, double value) /*-{
		this[index] = value;
	}-*/;
	

	/**
	 * Reset the length of the array.
	 * 
	 * @param newLength
	 *            the new length of the array
	 */
	public final native void setLength(int newLength) /*-{
		this.length = newLength;
	}-*/;

	/**
	 * Shifts the first value off the array.
	 * 
	 * @return the shifted value
	 */
	public final native int shiftInt() /*-{
		return this.shift();
	}-*/;
	/**
	 * Shifts the first value off the array.
	 * 
	 * @return the shifted value
	 */
	public final native boolean shiftBoolean() /*-{
		return this.shift();
	}-*/;
	/**
	 * Shifts the first value off the array.
	 * 
	 * @return the shifted value
	 */
	public final native String shiftString() /*-{
		return this.shift();
	}-*/;
	/**
	 * Shifts the first value off the array.
	 * 
	 * @return the shifted value
	 */
	public final native JavaScriptObject shiftObject() /*-{
		return this.shift();
	}-*/;
	/**
	 * Shifts the first value off the array.
	 * 
	 * @return the shifted value
	 */
	public final native double shiftDouble() /*-{
		return this.shift();
	}-*/;
	
	/**
	 * Shifts a value onto the beginning of the array.
	 * 
	 * @param value
	 *            the value to the stored
	 */
	public final native void unshift(int value) /*-{
		this.unshift(value);
	}-*/;
	/**
	 * Shifts a value onto the beginning of the array.
	 * 
	 * @param value
	 *            the value to the stored
	 */
	public final native void unshift(double value) /*-{
		this.unshift(value);
	}-*/;
	/**
	 * Shifts a value onto the beginning of the array.
	 * 
	 * @param value
	 *            the value to the stored
	 */
	public final native void unshift(String value) /*-{
		this.unshift(value);
	}-*/;
	/**
	 * Shifts a value onto the beginning of the array.
	 * 
	 * @param value
	 *            the value to the stored
	 */
	public final native void unshift(boolean value) /*-{
		this.unshift(value);
	}-*/;
	/**
	 * Shifts a value onto the beginning of the array.
	 * 
	 * @param value
	 *            the value to the stored
	 */
	public final native void unshift(JavaScriptObject value) /*-{
		this.unshift(value);
	}-*/;
	
//	public final native Collection<T> asCollection()/*-{
//		return @org.sgx.jsutil.client.JsUtil::toJavaCollection(Lcom/google/gwt/core/client/JavaScriptObject;)(this);
//	}-*/;
//
//	public final native Iterator<T> iterator()/*-{
//		var col = @org.sgx.jsutil.client.JsUtil::toJavaCollection(Lcom/google/gwt/core/client/JavaScriptObject;)(this);
//		return col.@java.util.Collection::iterator()();
//	}-*/;
	
	
	public final native String dump() /*-{
		return @org.sgx.jsutil.client.JsUtil::dumpArr2(Lcom/google/gwt/core/client/JavaScriptObject;)(this); 
	}-*/;
}
